package org.example;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 模拟并发订票
 */
public class SaleTicketDemo02 {
    public static void main(String[] args) {
        // 创建资源类，将资源类丢进线程
        Ticket2 ticket = new Ticket2();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) ticket.sale();
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) ticket.sale();
        }, "B").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) ticket.sale();
        }, "C").start();

        /* 不加锁情况：
         售票员B	出售第30张票	剩余29张
         售票员A	出售第30张票	剩余29张
         售票员C	出售第30张票	剩余29张
         现象解释：根据JMM内存模型，因为每个线程在自己的本地内存中都有一份变量副本，当多个线程同时修改一个资源时，
                  每个线程只是修改本地的副本，还未刷新到主内存中，导致数据未及时同步
         解决思路：将资源上锁，每次只允许被一个线程修改
         */
    }
}

/**
 * Lock
 * 三部曲：
 * 1. 创建ReentrantLock
 * 2. 上锁
 * 3. 解锁
 */
class Ticket2 {
    private int TICKET_NUM = 30;
    // 可重入锁，默认非公平
    private final ReentrantLock lock = new ReentrantLock();

    // 出售车票业务代码
    public synchronized void sale() {
        // 上锁
        lock.lock(); // new NonfairSync()
        try {
            System.out.println(Thread.currentThread().getName() + "出售第" + (TICKET_NUM--) + "张票，剩余：" + TICKET_NUM);
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } finally {
            // 解锁
            lock.unlock();
        }
    }
}